package ca.concordia.soen6011.commons;

/**
 * HELPER FUNCTIONS
 * -----------------------------------------------------
 * 
 * This is a common class to contain helper methods that
 * will be useful in all most of the implemented functions
 * 
 * @author musab.mirza
 * @since July-August 2014
 * 
 *        *****************************************************************
 *        TRANSIENT FUNCTION CALCULATOR is a team project for the SOEN 6011
 *        course delivered by Dr. Kamthan, Pankaj at Concordia University,
 *        Montreal for the summer 2014 session.
 *        *****************************************************************
 */
public class Helpers {

	public static double degree2radians(double x) {
		return (x / 180) * Math.PI;
	}

	public static boolean isInteger(double x) {
		if ((x == Math.floor(x)) && !Double.isInfinite(x)) {
			return true;
		}
		return false;
	}

	public static double logBase10(double x, double precision) throws Exception {
		return ln(x, precision) / ln(10, precision);
	}

	public static double factorial(int n) {
		if (n == 0)
			return 1;
		double fact = 1L;
		for (int c = 1; c <= n; c++)
			fact = fact * c;
		return fact;
	}

	public static double XOR(double x, double y) throws Exception {
		x = (int) x;
		y = (int) y;
		double result = 0;
		int significance = 1;
		while (x != 0 || y != 0) {
			result += significance * mod((x - y), 2);
			x = (int)x / 2;
			y = (int)y / 2;
			significance = significance * 2;
		}
		return result;
	}

	public static double ln(double x, double precision) throws Exception {
		if (x <= 0)
			return -1; // undefined
		if (x == 1)
			return 0;
		boolean biggerThanOne = false;
		if (x - 1 > 1) {
			biggerThanOne = true;
			x = 1 / x;
		}
		x = x - 1;
		double resultOfComputation = 0;
		for (int coEfficient = 1;; coEfficient++) {
			double polynomialTerm = power(x, coEfficient) / coEfficient;
			if (reachedRequiredPrecision(polynomialTerm, precision))
				break;
			resultOfComputation += (mod(coEfficient, 2) == 0) ? (polynomialTerm * (-1.0))
					: polynomialTerm;
		}
		if (!biggerThanOne) {
			return resultOfComputation;
		} else
		{
			return (-1)*resultOfComputation;
		}	
	}


	public static double mod(double dividend, double divisible) throws Exception {
		if (divisible == 0)
			throw new Exception("can't divide by zero");
		double result = (int) (dividend - ((int) (dividend / divisible))
				* divisible);
		return result;
	}

	public static double power(double x, int n) {
		if (n == 0)
			return 1;
		if (n == 1)
			return x;
		double result = x;
		for (int i = 1; i < Math.abs(n); i++) {
			result *= x;
		}

		if (n < 0)
			return 1 / result;
		else
			return result;
	}

	/**
	 * 
	 * This function compares two values and checks if the new value if the
	 * required amount of precision has reached
	 * 
	 * @param oldValue
	 * @param newValue
	 * @param requiredPrecision
	 * @return
	 */
	public static boolean reachedRequiredPrecision(double term,
			double requiredPrecision) {
		if (abs(term) < requiredPrecision)
			return true;
		return false;
	}
	
	public static double abs(double x)
	{
		if (x<0)
			return (-1)*x;
		return x;
	}
}
